package gov.nasa.anml.lifted;

import java.util.ArrayList;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.PDDL.BooleanExpression;
import gov.nasa.anml.PDDL.Effect;
import gov.nasa.anml.PDDL.FloatExpression;
import gov.nasa.anml.PDDL.Parameter;
import gov.nasa.anml.PDDL.Predicate;
import gov.nasa.anml.utility.SimpleFloat;
import gov.nasa.anml.utility.SimpleInteger;
import gov.nasa.anml.utility.SimpleString;

// use nulls to deal with order of evaluation?
// because i.start cannot be specified using i.end?
// so one has:
// [s,d]  s d null
// [s,e]  s null e
// [d,e]  null d e
// points:
// [s]  s null null
// ...s)  and extended ...s]  (but this is mostly [s])  ?
// (s...  and extended [s...  (ditto)					?
// []	null null null

public class IntervalImp implements Interval {

	public static Constant<SimpleInteger> constantBeforeBra;
	public static Constant<SimpleInteger> constantAtBra;
	public static Constant<SimpleInteger> constantAfterBra;
	public static Constant<SimpleInteger> constantBeforeKet;
	public static Constant<SimpleInteger> constantAtKet;
	public static Constant<SimpleInteger> constantAfterKet;
	public static Constant<SimpleFloat> constantDurationZero;
	public static Constant<SimpleFloat> constantDurationOne;

	// Constant is almost surely a better type than Expression, because the
	// definition of a temporal interval cannot itself be temporally varying.
	// In fact, that is so true I'm just going to change the type right now.
	// So for the comment to make sense, realize that previously 'Constant' was
	// 'Expression'.
	// this could be a sub-interval, sharing references with parent intervals.
	public Constant<SimpleFloat> start = null;
	public Constant<SimpleFloat> duration = null;
	public Constant<SimpleFloat> end = null;
	public Constant<SimpleInteger> bra = constantAtBra;
	public Constant<SimpleInteger> ket = constantAtKet;
	// dynamically computed bra/ket doesn't typically make sense.

	public transient ArrayList<PDDL.BooleanExpression> pddlConditions;
	public transient ArrayList<PDDL.Effect> pddlEffects;
	public transient ArrayList<PDDL.Parameter> pddlParameters;
	public transient PDDL.FloatExpression pddlDuration;
	public transient PDDL.Predicate pddlExecuting;
	public transient PDDL.Action pddlAction;
	
	public ArrayList<PDDL.BooleanExpression> getPDDLConditions() {
		return pddlConditions;
	}

	public FloatExpression getPDDLDuration() {
		return pddlDuration;
	}

	public ArrayList<Effect> getPDDLEffects() {
		return pddlEffects;
	}

	public Predicate getPDDLExecuting() {
		return pddlExecuting;
	}

	public ArrayList<Parameter> getPDDLParameters() {
		return pddlParameters;
	}

	public PDDL.Action getPDDLAction() {
		return pddlAction;
	}
	
	public Predicate makePDDLExecuting() {
		if (pddlExecuting == null)
			pddlExecuting = pddlAction.makeExecuting();
		return pddlExecuting;
	}
	
	
	public void copyPDDL(Interval unit) {
		pddlConditions = unit.getPDDLConditions();
		pddlEffects = unit.getPDDLEffects();
		pddlParameters = unit.getPDDLParameters();
		pddlDuration = unit.getPDDLDuration();
		pddlExecuting = unit.getPDDLExecuting();
		pddlAction = unit.getPDDLAction();
	}

	public IntervalImp() {
	}

	public IntervalImp(int b, int k) {
		setShape(b, k);
	}

	// fully specified interval.
	// start + duration == end had better hold...
	public IntervalImp(int b, Constant<SimpleFloat> s, Constant<SimpleFloat> d,
			Constant<SimpleFloat> e, int k) {
		setShape(b, k);
		this.start = s;
		this.duration = d;
		this.end = e;
	}

	// fully specified interval of the 'normal' kind; i.e., ? s, ^d ?
	// that is, a dispatch time and a duration, e.g., actions.
	public IntervalImp(int b, Constant<SimpleFloat> s, Constant<SimpleFloat> d,
			int k) {
		setShape(b, k);
		this.start = s;
		this.duration = d;
		inferEnd();
	}

	// dynamic shape!
	public IntervalImp(Constant<SimpleInteger> b, Constant<SimpleFloat> s,
			Constant<SimpleFloat> d, Constant<SimpleFloat> e,
			Constant<SimpleInteger> k) {
		this.bra = b;
		this.start = s;
		this.duration = d;
		this.end = e;
		this.ket = k;
	}

	// makes a point
	public IntervalImp(Constant<SimpleFloat> t) {
		this.start = t;
		this.duration = constantDurationZero;
		this.end = t;
	}

	public IntervalImp(IntervalImp i) {
		this.start = i.start;
		this.duration = i.duration;
		this.end = i.end;
		this.bra = i.bra;
		this.ket = i.ket;
	}

	public IntervalImp(Interval u) {
		this.start = u.getStart();
		this.duration = u.getDuration();
		this.end = u.getEnd();
		this.bra = u.getBra();
		this.ket = u.getKet();
	}

	public void setShape(int b, int k) {
		setBra(b);
		setKet(k);
	}
	
	public void setShape(Expression b, Expression k) {
		makeBra(b);
		makeKet(k);
	}

	public static final Constant<SimpleInteger> makeBra(int b) {
		switch (b) {
		case Before:
			// Not a fantastic concept. For points, ...t] is the notation.
			// for intervals, ...s,t] is I think right.
			return constantBeforeBra;
		case At:
			return constantAtBra;
		case After:
			return constantAfterBra;
		default:
			System.err.println("Invalid Time Code");
			return null;
		}
	}
	
	public void setBra(int b) {
		bra = makeBra(b);
	}

	public static final Constant<SimpleInteger> makeKet(int k) {
		switch (k) {
		case Before:
			return constantBeforeKet;
		case At:
			return constantAtKet;
		case After:
			// Not a fantastic concept, but I guess the notation is [t... for points
			// for intervals, [s,t... might be acceptable? Probably.
			return constantAfterKet;
		default:
			System.err.println("Invalid Time Code");
			return null;
		}
	}

	public void setKet(int k) {
		ket = makeKet(k);
	}
	
	public void inferStart() {
		start = new Constant<SimpleFloat>(startName, Unit.floatType,
				new OpBinary(TypeCode.Float, Op.subtract, end, duration));
	}

	public void inferDuration() {
		duration = new Constant<SimpleFloat>(durationName, Unit.floatType,
				new OpBinary(TypeCode.Float, Op.subtract, end, start));
	}

	public void inferEnd() {
		end = new Constant<SimpleFloat>(endName, Unit.floatType, new OpBinary(
				TypeCode.Float, Op.add, start, duration));
	}

	public void set(Constant<SimpleFloat> s, Constant<SimpleFloat> d,
			Constant<SimpleFloat> e, Constant<SimpleInteger> b,
			Constant<SimpleInteger> k) {
		start = s;
		duration = d;
		end = e;
		bra = b;
		ket = k;
	}

	public void set(IntervalImp i) {
		start = i.start;
		duration = i.duration;
		end = i.end;
		bra = i.bra;
		ket = i.ket;
	}
	
	public void set(Interval i) {
		start = i.getStart();
		duration = i.getDuration();
		end = i.getEnd();
		bra = i.getBra();
		ket = i.getKet();
	}
	
	/*
	 * public void split() { int k = ket.value(null).v; int b =
	 * bra.value(null).v; int l = k - b + 3; pieces = new AtomicTime[l]; if (b <
	 * 1) { pieces[0] = new Point(start,b); ++b; if (b < 1) { pieces[1] = new
	 * Point(start,b); pieces[2] = new OpenInterval(start,end); b=3; } else {
	 * pieces[1] = new OpenInterval(start,end); ++b; } } else { pieces[0] = new
	 * OpenInterval(start,end); } if (k >= 0) { pieces[b] = new Point(end,0);
	 * ++b; if (k >= 1) { pieces[b] = new Point(end,1); } } }
	 * 
	 * public AtomicTime getPiece(int k) { return pieces[k]; }
	 */

	public Constant<SimpleFloat> getStart() {
		return start;
	}

	public Constant<SimpleFloat> getDuration() {
		return duration;
	}

	public Constant<SimpleFloat> getEnd() {
		return end;
	}

	public Constant<SimpleInteger> getBra() {
		return bra;
	}

	public Constant<SimpleInteger> getKet() {
		return ket;
	}

	public void setStart(Constant<SimpleFloat> start) {
		this.start = start;
	}

	public void setDuration(Constant<SimpleFloat> duration) {
		this.duration = duration;
	}

	public void setEnd(Constant<SimpleFloat> end) {
		this.end = end;
	}

	public void setBra(Constant<SimpleInteger> bra) {
		this.bra = bra;
	}

	public void setKet(Constant<SimpleInteger> ket) {
		this.ket = ket;
	}

	public void makeBra(Expression<SimpleInteger, ?> b) {
		if (b instanceof Constant)
			bra = (Constant<SimpleInteger>) b;
		else
			bra = new Constant<SimpleInteger>(braName,Unit.integerType,b);
	}
	public void makeKet(Expression<SimpleInteger, ?> k) {
		if (k instanceof Constant)
			ket = (Constant<SimpleInteger>) k;
		else
			ket = new Constant<SimpleInteger>(ketName,Unit.integerType,k);
	}
	public void makeStart(Expression<SimpleFloat, ?> s) {
		if (s instanceof Constant)
			start = (Constant<SimpleFloat>) s;
		else
			start = new Constant<SimpleFloat>(startName,Unit.floatType,s);
	}
	public void makeDuration(Expression<SimpleFloat, ?> d) {
		if (d instanceof Constant)
			duration = (Constant<SimpleFloat>) d;
		else
			duration = new Constant<SimpleFloat>(durationName,Unit.floatType,d);
	}
	public void makeEnd(Expression<SimpleFloat, ?> e) {
		if (e instanceof Constant)
			end = (Constant<SimpleFloat>) e;
		else
			end = new Constant<SimpleFloat>(endName,Unit.floatType,e);
	}

}
